#include "syscontroller.h"
#include <stdio.h>
#include "program.h"
#include "tabledriver.h"

#include <iostream>		// FOR TEST ONLY


SysController *SysController::instance_ = NULL;

namespace {

// convert string vector to QStringList
QStringList StringListFromStdVector(const std::vector<std::string>& str_vector)
{
	QStringList result;
	for (unsigned int index = 0; index < str_vector.size(); ++index) {
		result << QString::fromStdString(str_vector.at(index));
	}
	return result;
}

}


SysController::SysController()
	: pm_(ProgramManager::instance())
{
}

SysController *SysController::instance()
{
	if (NULL == instance_) {
		instance_ = new SysController;
	}
	return instance_;
}

int SysController::itemCount() const
{
	return pm_->itemCount();
}

QStringList SysController::ItemsList() const
{
	return StringListFromStdVector(TableDriver::ParamList(PM_ITEM_ATTR));
}
QStringList SysController::DevicesList() const
{
	return StringListFromStdVector(TableDriver::ParamList(PM_DEV_ATTR));
}
QStringList SysController::FilterOptList() const
{
	return StringListFromStdVector(TableDriver::ParamList(PM_FIL_ATTR));
}
QStringList SysController::ChannelList() const
{
	QStringList channel_list;
	int max_ch = deviceType().left(1).toInt();
	for (int ch = 0; ch < max_ch; ++ch) {
		channel_list << QString::number(ch + 1);
	}
	return channel_list;
}
QStringList SysController::OnOffList() const
{
	return StringListFromStdVector(TableDriver::ParamList(PM_ONOFF_ATTR));
}
QStringList SysController::ItemRangeList(int item_index, bool bias) const
{
	return StringListFromStdVector(TableDriver::ItemRangeList(
						pm_->itemCode(item_index), 
						(bias ? PM_BRANGE_ATTR : PM_MRANGE_ATTR)));
}

bool SysController::IsMasterItem(int item_index) const
{
	return TableDriver::IsMasterItem(pm_->itemCode(item_index));
}
bool SysController::IsEsdItem(int item_index) const
{
	return (pm_->itemCode(item_index) == PM_ESD);
}
bool SysController::IsDlmItem(int item_index) const
{
	return TableDriver::IsDlmItem(pm_->itemCode(item_index));
}

double SysController::RangeUpper(int type) const
{
	if (1 == type) {
		return TableDriver::Range(PM_T9999ms)->hilimit;
	} else if (2 == type) {
		return TableDriver::Range(PM_L9_99V)->hilimit;
	}
	return 0.0;
}
double SysController::RangeLower(int type) const
{
	if (1 == type) {
		return TableDriver::Range(PM_T9999ms)->lowlimit;
	} else if (2 == type) {
		return TableDriver::Range(PM_L9_99V)->lowlimit;
	}
	return 0.0;
}
double SysController::RangeStep(int type) const
{
	if (1 == type) {
		return TableDriver::Range(PM_T9999ms)->step;
	} else if (2 == type) {
		return TableDriver::Range(PM_L9_99V)->step;
	}
	return 0.0;
}
bool SysController::IsRangeInteger(int type) const
{
	return (RangeStep(type) >= 1.0);
}

double SysController::ItemRangeUpper(int item_index, bool bias) const
{
	ushort rng_code =  bias ? pm_->itemBiasRng(item_index) 
							: pm_->itemMeasRng(item_index);
	return TableDriver::Range(rng_code)->hilimit;
}
double SysController::ItemRangeLower(int item_index, bool bias) const
{
	ushort rng_code =  bias ? pm_->itemBiasRng(item_index) 
							: pm_->itemMeasRng(item_index);
	return TableDriver::Range(rng_code)->lowlimit;
}
double SysController::ItemRangeStep(int item_index, bool bias) const
{
	ushort rng_code =  bias ? pm_->itemBiasRng(item_index) 
							: pm_->itemMeasRng(item_index);
	return TableDriver::Range(rng_code)->step;
}
bool SysController::IsItemRangeInteger(int item_index, bool bias) const
{
	return (ItemRangeStep(item_index, bias) >= 1.0);
}
QStringList SysController::RangeList(int type) const
{
	std::vector<std::string> range_list;
	if (3 == type) {
		range_list = TableDriver::RangeList(PM_HUERNG_ATTR, '0', '0');
	} else if (4 == type) {
		range_list = TableDriver::RangeList(PM_HUESCF_ATTR, '0', '0');
	}
	return StringListFromStdVector(range_list);
}

int SysController::LoadProgram(const QString &path)
{
	return pm_->LoadProgram(path.toStdString());
}

int SysController::SaveProgram(const QString &path)
{
	return pm_->SaveProgram(path.toStdString());
}


void SysController::NewProgram()
{
	pm_->InitProgram();
}
QString SysController::programFilePath() const
{
	return QString::fromStdString(pm_->programFilePath());
}

int SysController::InsertItem(int item_index, const QString& item_name)
{
	return pm_->InsertItem(item_index, 
			TableDriver::ParamCode(item_name.toStdString(), PM_ITEM_ATTR));
}

int SysController::RemoveItem(int item_index)
{
	return pm_->RemoveItem(item_index);
}

bool SysController::IsProgramDirty() const
{
	return pm_->IsChangedAndUnsaved();
}

int SysController::setDeviceType(const QString& dev_name)
{
	return pm_->setDeviceType(
			TableDriver::ParamCode(dev_name.toStdString(), PM_DEV_ATTR));
}
QString SysController::deviceType() const
{
	return QString::fromStdString(TableDriver::ParamName(pm_->deviceType()));
}

QString SysController::FormatValueString(ushort rng_code, double value) const
{
	QString val_str;
	const rng_tbl_s *range = TableDriver::Range(rng_code);	
	if (range != NULL) {
		if (range->step < 1.0) {
			// decimals
			char sz_val[kValStrLenMax];
			sprintf(sz_val, range->dfmt, value);
			val_str = sz_val;
		} else {
			// integer	
			// at this moment, I use this ugly cast here!!
			val_str = QString::number((int)value);	
		}
	}
	return val_str;
}


int SysController::setMeasOptMeasMode(const QString& meas_mode)
{
	ushort mmode_code = TableDriver::ParamCode(
								meas_mode.toStdString(), PM_MMODE_ATTR);
	return pm_->setMeasOptMeasMode(mmode_code);
}
QString SysController::measOptMeasMode() const
{
	return QString::fromStdString(
				TableDriver::ParamName(pm_->measOptMeasMode()));	
}
int SysController::setMeasOptFfs(bool enable)
{
	return pm_->setMeasOptFfs(enable);
}
bool SysController::measOptFfs() const
{
	return pm_->measOptFfs();
}
int SysController::setMeasOptCntClr(bool enable)
{
	return pm_->setMeasOptCntClr(enable);
}
bool SysController::measOptCntClr() const
{
	return pm_->measOptCntClr();
}
int SysController::setMeasOptDataFile(bool enable)
{
	return pm_->setMeasOptDataFile(enable);
}
bool SysController::measOptDataFile() const
{
	return pm_->measOptDataFile();
}




int SysController::setOptEndTime(const QString& end_time)
{
	return pm_->setOptEndTime(end_time.toDouble());
}
QString SysController::optEndTime() const
{
	return FormatValueString(PM_T99_9ms, pm_->optEndTime());
}
int SysController::setOptOffTime(const QString& off_time)
{
	return pm_->setOptOffTime(off_time.toDouble());
}
QString SysController::optOffTime() const
{
	return FormatValueString(PM_T99_9ms, pm_->optOffTime());
}
int SysController::setOptWarnNgCnt(const QString& warn_ng_cnt)
{
	return pm_->setOptWarnNgCnt(warn_ng_cnt.toInt());
}
QString SysController::optWarnNgCnt() const
{
	return FormatValueString(PM_COUNT, pm_->optWarnNgCnt());
}
int SysController::setOptFilterFreq(const QString& fil_freq)
{
	return pm_->setOptFilterFreq(
		TableDriver::ParamCode(fil_freq.toStdString(), PM_FILFREQ_ATTR));
}
QString SysController::optFilterFreq() const
{
	return QString::fromStdString(
				TableDriver::ParamName(pm_->optFilterFreq()));
}
int SysController::setOptHdrLogic(const QString& hdr_logic)
{
	return pm_->setOptHdrLogic(hdr_logic.toInt());
}
QString SysController::optHdrLogic() const
{
	return FormatValueString(PM_LOGIC, pm_->optHdrLogic());
}
int SysController::setOptDlmCoef(const QString& coef)
{
	return pm_->setOptDlmCoef(coef.toDouble());
}
QString SysController::optDlmCoef() const
{
	return FormatValueString(PM_DLMADJ, pm_->optDlmCoef());
}
int SysController::setOptSelStep(bool enable)
{
	return pm_->setOptSelStep(enable);
}
bool SysController::optSelStep() const
{
	return pm_->optSelStep();
}


// this function is dirty!
int SysController::changeItemChannel(int item_index)
{
	char ch = TableDriver::ParamName(pm_->program()->kinds[0].devicetyp).at(0);
	int max_ch = 1;
	if ('1' == ch) {
		max_ch = 1;	
	} else if ('2' == ch) {
		max_ch = 2;	
	} else if ('3' == ch) {
		max_ch = 3;	
	}
	item_s *item = pm_->item(item_index);
	if (NULL == item) {
		return -1;
	}
	int curr_ch = item->ch;
	curr_ch += 1;
	if (curr_ch > max_ch) {
		curr_ch -= max_ch;
	}
	item->ch = curr_ch;	

	return 0;
}
bool SysController::ItemHasDetail(int item_index) const
{
	item_s *item = pm_->item(item_index);
	if (NULL == item) {
		return false;
	}
	return (TableDriver::IsMasterItem(item->code) || (PM_ESD == item->code));
}
int SysController::setItemChannel(int item_index, const QString& item_ch_sfx)
{
	// get channel string from the item name_ch_sfx string
	QString ch_str = item_ch_sfx.left(item_ch_sfx.indexOf(")")).right(1);
	return pm_->setItemChannel(item_index, ch_str.toInt());
}
int SysController::setItemChannelEx(int item_index, const QString& ch)
{
	return pm_->setItemChannel(item_index, ch.toInt());
}
QString SysController::itemChannel(int item_index) const
{
	return QString::number(pm_->itemChannel(item_index));
}
QString	SysController::itemNameChnSuffix(int item_index) const
{
	QString name_ch_sfx = QString::fromStdString(
						  TableDriver::ParamName(pm_->itemCode(item_index)))
						+ pm_->itemSuffix(item_index) + "("
						+ QString::number(pm_->itemChannel(item_index)) + ")";
	return name_ch_sfx;
}
int SysController::setItemWaitTime(int item_index, const QString& wait_time)
{
	return pm_->setItemWaitTime(item_index, wait_time.toDouble());
}
QString SysController::itemWaitTime(int item_index) const
{
	return FormatValueString(PM_T9999ms, pm_->itemWaitTime(item_index));
}
int SysController::setItemBiasRng(int item_index, const QString& rng_name)
{
	return pm_->setItemBiasRng(item_index, 
			TableDriver::ParamCode(rng_name.toStdString(), PM_BRANGE_ATTR));
	// FIXME: I've writen PM_MRAGNE_ATTR before, check other places
	// to prevent this kind mistakes
}
QString SysController::itemBiasRng(int item_index) const
{
	return QString::fromStdString(
			TableDriver::ParamName(pm_->itemBiasRng(item_index)));
}
int SysController::setItemBiasVal(int item_index, const QString& val)
{
	return pm_->setItemBiasVal(item_index, val.toDouble());
}
QString SysController::itemBiasVal(int item_index) const
{
	return FormatValueString(
			pm_->itemBiasRng(item_index), pm_->itemBiasVal(item_index));
}
int SysController::setItemMeasRng(int item_index, const QString& rng_name)
{
	return pm_->setItemMeasRng(item_index,
			TableDriver::ParamCode(rng_name.toStdString(), PM_MRANGE_ATTR));
}
QString SysController::itemBiasValUnit(int item_index) const
{
	return (itemBiasVal(item_index) + " "
			+ TableDriver::Range(pm_->itemBiasRng(item_index))->unit);
}
QString SysController::itemMeasRng(int item_index) const
{
	return QString::fromStdString(
			TableDriver::ParamName(pm_->itemMeasRng(item_index)));
}
int SysController::setItemMeasVal(
		int item_index, bool lower, const QString& val)
{
	return pm_->setItemMeasVal(item_index, lower, val.toDouble());
}

QString SysController::itemMeasVal(int item_index, bool lower) const
{
	return FormatValueString(
			pm_->itemMeasRng(item_index), pm_->itemMeasVal(item_index, lower));
}
QString SysController::itemMeasValUnit(int item_index) const
{
	return (itemMeasVal(item_index, true) + "~"
			+ itemMeasVal(item_index, false) + " "
			+ TableDriver::Range(pm_->itemMeasRng(item_index))->unit);
}
int SysController::setItemVolLimit(int item_index, const QString& limit)
{
	return pm_->setItemVolLimit(item_index, limit.toDouble());
}
QString SysController::itemVolLimit(int item_index) const
{
	return FormatValueString(PM_L9_99V, pm_->itemVolLimit(item_index));
}
int SysController::setItemLimitJudge(int item_index, const QString& onoff)
{
	return pm_->setItemLimitJudge(item_index, ("ON" == onoff));
}
QString SysController::itemLimitJudge(int item_index) const
{
	return (pm_->itemLimitJudge(item_index) ? "ON" : "OFF");
}
int SysController::changeItemLimitJudge(int item_index)
{
	return pm_->setItemLimitJudge(item_index, !pm_->itemLimitJudge(item_index));
}
int SysController::setItemFilterOpt(int item_index, const QString &fil_opt)
{
	return pm_->setItemFilterOpt(item_index, 
			TableDriver::ParamCode(fil_opt.toStdString(), PM_FIL_ATTR));
}
QString SysController::itemFilterOpt(int item_index)
{
	return QString::fromStdString(
			TableDriver::ParamName(pm_->itemFilterOpt(item_index)));
}
//// another ugly func...
//int SysController::changeItemFilterOpt(int item_index)
//{
//	ushort fil_code = pm_->itemFilterOpt();
//	fil_code += 1;
//	if (fil_code > 
//	return pm_->setItemFilterOpt(item_index, fil_code);
//}

QStringList SysController::RefItemList(int item_index) const
{
	QStringList result;
	std::vector<int> itm_idx_list = pm_->RefItemList(item_index);
	for (int v_idx = 0; v_idx < itm_idx_list.size(); ++v_idx) {
		result << itemNameChnSuffix(itm_idx_list.at(v_idx));
	}
	return result;
}

int SysController::setEsdRefItem(
		int itm_idx, int ref_idx, const QString& item_ch_sfx)
{
	// NOTE!: be careful about this statements
	QString item_name_sfx = item_ch_sfx.left(item_ch_sfx.indexOf("("));
	QString item_name	= item_name_sfx.left(item_name_sfx.size() - 1);
	QString item_suffix	= item_name_sfx.right(1);
	int item_index = pm_->itemIndex(item_name.toStdString(), 
								    item_suffix.at(0).toAscii());
	return pm_->setEsdRefItem(itm_idx, ref_idx, item_index);
}
QString SysController::esdRefItem(int itm_idx, int ref_idx) const
{
	QString name_ch_sfx;
	item_s *item = pm_->item(pm_->esdRefItem(itm_idx, ref_idx));
	if (NULL != item) {
		name_ch_sfx = QString::fromStdString(
							TableDriver::ParamName(item->code))
					+ item->suffix + ("(")
					+ QString::number(item->ch) + (")");
	}
	return name_ch_sfx;
}


int SysController::setHueAutoRange(int item_index, bool enable)
{
	return pm_->setHueAutoRange(item_index, enable);
}
bool SysController::hueAutoRange(int item_index) const
{
	return pm_->hueAutoRange(item_index);
}
int SysController::setHueOfstCancel(int item_index, bool enable)
{
	return pm_->setHueOfstCancel(item_index, enable);
}
bool SysController::hueOfstCancel(int item_index) const
{
	return pm_->hueOfstCancel(item_index);
}
int SysController::setHuePreAmpRng(
		int item_index, int pre_amp_idx, const QString& rng_name)
{
	return pm_->setHuePreAmpRng(item_index, pre_amp_idx, 
			TableDriver::ParamCode(rng_name.toStdString(), PM_HUERNG_ATTR));
}
QString SysController::huePreAmpRng(int item_index, int pre_amp_idx) const
{
	return QString::fromStdString(
		TableDriver::ParamName(pm_->huePreAmpRng(item_index, pre_amp_idx)));
}
int SysController::setHueAmpScf(
		int item_index, int amp_idx, const QString& scf_name)
{
	return pm_->setHueAmpScf(item_index, amp_idx, 
			TableDriver::ParamCode(scf_name.toStdString(), PM_HUESCF_ATTR));
}
QString SysController::hueAmpScf(int item_index, int amp_idx) const
{
	return QString::fromStdString(
		TableDriver::ParamName(pm_->hueAmpScf(item_index, amp_idx)));
}
